package view;

import graphics.engine.ProgramShader;
import graphics.engine.TextureManager;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;

import com.sun.opengl.util.j2d.TextRenderer;

import controler.Lasso;

import settings.Settings;
import utils.AlignedBox3D;
import utils.Pair;
import utils.Point3D;
import utils.Ray3D;
import utils.TimeIntervalle;
import utils.TrajectoryComparator;
import view.trajectory.BaseTrajectoryView;
import view.trajectory.RoomTrajectoryView;
import view.trajectory.TargetView;
import view.trajectory.TrajectoryView;
import view.trajectory.TransitionTrajectoryView;

import mathematics.geometry.Geometry;
import model.AdjacencyModel;
import model.DoorModel;
import model.MainModel;
import model.RoomModel;
import model.trajectory.RoomTrajectoryModel;
import model.trajectory.TargetModel;
import model.trajectory.TransitionTrajectoryModel;

public class MainView {
	
	public MainModel model;
	
	public ArrayList<RoomView> rooms;
	public ArrayList<TargetView> targets;
	public ArrayList<DoorView> doors;

	public boolean isInitialized = false;

	private boolean trajectoryesHaveChanged;
	
	public MainView(MainModel model)
	{
		this.model = model;
		this.rooms = new ArrayList<RoomView>();
		this.doors = new ArrayList<DoorView>();
		this.targets = new ArrayList<TargetView>();
	}
	
	public void Init(GL gl, TextRenderer textRenderer, TextureManager textureManager) throws IOException
	{
		for(Iterator<RoomModel> it = model.rooms.iterator(); it.hasNext();)
		{
			RoomModel roomModel = it.next();
			RoomView roomView = new RoomView(gl, textRenderer, roomModel);
			if(roomModel.hasTexture())
			{
				textureManager.add(gl, roomModel.getTexturePath());
				roomView.setTexture(textureManager.get(roomModel.getTexturePath()));
			}
			rooms.add(roomView);
		}
		
		for(Iterator<RoomModel> it = model.rooms.iterator(); it.hasNext();)
		{
			RoomModel roomModel = it.next();
			RoomView roomView = (RoomView) this.getViewById(roomModel.getID()); 
			
			for(Iterator<DoorModel> itDoors = roomModel.doors.iterator(); itDoors.hasNext();)
			{
				DoorModel doorModel = itDoors.next();
				DoorView doorView = new DoorView(doorModel, roomView, (RoomView) this.getViewById(doorModel.SecondRoom.getID()));
				doors.add(doorView);
				roomView.AddDoor(doorView);
			}
			for(Iterator<AdjacencyModel> itAdjacencies = roomModel.adjacencies.iterator(); itAdjacencies.hasNext();)
			{
				AdjacencyModel adjacencyModel = itAdjacencies.next();
				roomView.AddAdjacency(new AdjacencyView(adjacencyModel, roomView, (RoomView) this.getViewById(adjacencyModel.SecondRoom.getID())));
			}
		}
		for(Iterator<TargetModel> itTarg = model.targets.iterator(); itTarg.hasNext();)
		{
			TargetModel targetModel = itTarg.next();
			TrajectoryView trajView = new TrajectoryView(gl, targetModel.traj, rooms, doors);
			this.targets.add(new TargetView(gl, targetModel, trajView));
		}
		
		for(Iterator<TargetView> itTarg = this.targets.iterator(); itTarg.hasNext();)
		{
			for(Iterator<Pair<TimeIntervalle, BaseTrajectoryView>> itTraj = itTarg.next().trajView.subTrajectoryViews.iterator(); itTraj.hasNext();)
			{
				BaseTrajectoryView baseView = itTraj.next().value;
				if(baseView.getClass() == RoomTrajectoryView.class)
				{
					RoomTrajectoryView roomTrajView = (RoomTrajectoryView)baseView;
					rooms.get(rooms.indexOf(roomTrajView.room)).addSubtrajectoryView(roomTrajView);
				}
				else if(baseView.getClass() == TransitionTrajectoryView.class)
				{
					TransitionTrajectoryView transitionTrajView = (TransitionTrajectoryView)baseView;
					transitionTrajView.door.addSubtrajectoryView(transitionTrajView);
					//rooms.get(rooms.indexOf(transitionTrajView.firstRoom)).addSubtrajectoryView(transitionTrajView);
					//rooms.get(rooms.indexOf(transitionTrajView.secondRoom)).addSubtrajectoryView(transitionTrajView);
				}
			}
		}
		
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext();)
		{
			RoomView room = it.next();
			room.setNodeShape(true);
			TrajectoryComparator comparator =  new TrajectoryComparator();
			Collections.sort(room.getSubtrajectories(), comparator);
		}
		
		for(Iterator<DoorView> it = doors.iterator(); it.hasNext();)
		{
			DoorView door = it.next();
			TrajectoryComparator comparator =  new TrajectoryComparator();
			Collections.sort(door.getSubtrajectories(), comparator);
		}
		
		if(Settings.AutoCollapseAlgo)
		{
			//this.applyAutoCollapseAlgo(this.model.minTime, this.model.maxTime);
		}
		this.trajectoryesHaveChanged(true);
		this.isInitialized = true;
	}
	
	private void applyAutoCollapseAlgo(long begin, long end) {
		for(Iterator<RoomView> itRoom = rooms.iterator(); itRoom.hasNext();)
		{
			RoomView room = itRoom.next();
			if(((RoomModel) room.getModel()).targetsInsideRoom(begin, end).size() <= 1 || ((RoomModel)room.getModel()).isCorridor())
			{
				room.setNodeShape(true);
			}
		}
	}
	
	public BaseHybridView getViewById(int ID)
	{
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext();)
		{
			RoomView view = it.next();
			if(view.getModel().getID() == ID)
			{
				return view;
			}
		}
		return null;
	}

	public void collapseAllRooms() {
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext(); )
		{
			it.next().setNodeShape(true);
		}
	}

	public void DrawTrajectories(GLAutoDrawable drawable, ProgramShader programShader, long logTime, long beginTime, long endTime) {

		GL gl = drawable.getGL();

		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			RoomView view = it.next();
			for(Iterator<RoomTrajectoryView> itTraj  = view.getSubtrajectories().iterator(); itTraj.hasNext(); )
			{
				RoomTrajectoryView traj = itTraj.next();
				if( ((RoomTrajectoryModel)traj.getModel()).isVisible() )
				{
					traj.Draw(gl, programShader, logTime, beginTime, endTime);
				}
			}
		}
		
		for(Iterator<DoorView> it = this.doors.iterator(); it.hasNext();)
		{
			DoorView view = it.next();
			for(Iterator<TransitionTrajectoryView> itTraj  = view.getSubtrajectories().iterator(); itTraj.hasNext(); )
			{
				TransitionTrajectoryView traj = itTraj.next();
				if( ((TransitionTrajectoryModel)traj.getModel()).isVisible() )
				{
					itTraj.next().Draw(gl, programShader, logTime, beginTime, endTime);
				}
			}
		}
	}
	
	public void DrawTargets(GLAutoDrawable drawable, ProgramShader programShader, long time) {
		for(Iterator<TargetView> it = this.targets.iterator(); it.hasNext();)
		{
			TargetView view = it.next();
			if(!view.model.isHighlighted() && !view.model.isSelected)
			{
				view.Draw(drawable, programShader, time);
			}
		}
	}
	
	public void Draw(GLAutoDrawable drawable, ProgramShader programShader)
	{
		if(Settings.DrawAjacencies)
		{
			this.DrawAdjacencies(drawable, programShader);
		}
		if(Settings.DrawRooms)
		{
			this.DrawRooms(drawable, programShader);
		}
		if(Settings.DrawSelection)
		{
			this.DrawSelection(drawable, programShader);
		}
		if(Settings.DrawDoors)
		{
			this.DrawDoors(drawable, programShader);
		}
		if(Settings.DrawRooms)
		{
			this.DrawNodes(drawable, programShader);
		}
	}
	

	public void DrawNodes(GLAutoDrawable drawable, ProgramShader programShader)
	{
		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			RoomView room = it.next();
			if(room.isNodeShape())
			{
				room.Draw(drawable, programShader);
			}
		}	
	}
	
	public void DrawRooms(GLAutoDrawable drawable, ProgramShader programShader)
	{
		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			RoomView room = it.next();
			if(!room.isNodeShape())
			{
				room.Draw(drawable, programShader);
			}		
		}	
	}


	public void DrawDoors(GLAutoDrawable drawable, ProgramShader programShader)
	{
		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			it.next().DrawDoors(drawable, programShader);
		}		
	}
	
	public void DrawAdjacencies(GLAutoDrawable drawable, ProgramShader programShader)
	{
		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			it.next().DrawAdjacencies(drawable, programShader);
		}		
	}
	
	public void DrawSelection(GLAutoDrawable drawable, ProgramShader programShader)
	{
		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			it.next().DrawSelection(drawable, programShader);
		}		
	}

	public ArrayList<BaseHybridView> intersectedNodes(Ray3D ray) 
	{
		ArrayList<BaseHybridView> result = new ArrayList<BaseHybridView>();

		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			result.addAll(it.next().intersectedNodes(ray));
		}		
		return result;
	}
	
	
	public ArrayList<RoomView> intersectedNodes(Lasso lasso)
	{
		ArrayList<RoomView> result = new ArrayList<RoomView>();

		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			RoomView room = it.next();
			if(room.isIntersectedBy(lasso))
			{
				result.add(room);
			}
		}
		return result;
	}

	public AlignedBox3D getBoundingBox() 
	{
		ArrayList<Point3D> points = new ArrayList<Point3D>();
		for(Iterator<RoomView> it = this.rooms.iterator(); it.hasNext();)
		{
			points.addAll(it.next().getAllPoints());
		}
		AlignedBox3D box = new AlignedBox3D();
		box.bound(points);
		return box;
	}

	public void clearMouseOver() 
	{
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext();)
		{
			it.next().clearMouseOver();
		}
	}

	public void clearSelection() 
	{
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext();)
		{
			it.next().clearSelection();
		}		
	}
	
	public void resetStat()
	{
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext();)
		{
			it.next().resetStat();
		}		
	}

	public ArrayList<TargetModel> getIntersectedTrajectories(Geometry selectionDisk) {
		// TODO Auto-generated method stub
		ArrayList<TargetModel> result = new ArrayList<TargetModel>();
		for(Iterator<TargetView> it = targets.iterator(); it.hasNext();)
		{
			TargetView view = it.next();
			if(view.isIntersectedBy(selectionDisk))
			{
				result.add(view.model);
			}
		}
		return result;
	}

	public void DrawRoomLabel(GLAutoDrawable drawable) {
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext();)
		{
			it.next().drawLabel(drawable);
		}
	}
	
	public void trajectoryesHaveChanged(boolean value)
	{
		this.setTrajectoryesHaveChanged(value);
	}

	public void expendAllRooms() {
		// TODO Auto-generated method stub
		for(Iterator<RoomView> it = rooms.iterator(); it.hasNext(); )
		{
			it.next().setNodeShape(false);
		}
	}

	public boolean isTrajectoryesHaveChanged() {
		return trajectoryesHaveChanged;
	}

	public void setTrajectoryesHaveChanged(boolean trajectoryesHaveChanged) {
		this.trajectoryesHaveChanged = trajectoryesHaveChanged;
	}
}
